#
# Ronin Exploits - A Ruby library for Ronin that provides exploitation and
# payload crafting functionality.
#
# Copyright (c) 2007-2013 Hal Brodigan (postmodern.mod3 at gmail.com)
#
# This file is part of Ronin Exploits.
#
# Ronin is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# Ronin is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with Ronin.  If not, see <http://www.gnu.org/licenses/>
#

require 'ronin/model'
require 'ronin/arch'

module Ronin
  module Model
    #
    # Adds a `arch` relation between a model and the `Ronin::Arch` model.
    #
    module TargetsArch
      def self.included(base)
        base.send :include, InstanceMethods
        base.send :extend, ClassMethods

        base.module_eval do
          # The targeted architecture
          belongs_to :arch,
                     model: 'Ronin::Arch',
                     required: false
        end

        Arch.has Arch.n, base.relationship_name, model: base.name
      end

      module ClassMethods
        #
        # Queries all resources targeting a specific Architecture.
        #
        # @param [Arch, Hash, Symbol, #to_s] arch
        #   The architecture to search for.
        #
        # @return [DataMapper::Collection]
        #   The matching resources.
        #
        # @since 1.0.0
        #
        # @api public
        #
        def targeting_arch(arch)
          conditions = case arch
                       when Arch, Hash
                         {arch: arch}
                       when Symbol
                         unless Arch.methods(false).include?(arch)
                           raise(ArgumentError,"unknown arch: #{arch}")
                         end

                         {arch: Arch.send(arch)}
                       else
                         {'arch.name' => arch.to_s}
                       end

          all(conditions)
        end
      end

      module InstanceMethods
        #
        # Sets the targeted architecture.
        #
        # @param [Symbol] name
        #   The name of the new Arch to target.
        #
        # @return [Arch]
        #   The architecture to target.
        #
        # @example
        #   t.targets_arch :x86
        #   # => #<Ronin::Arch: x86>
        #
        # @since 1.0.0
        #
        def targets_arch(name)
          unless Arch.methods(false).include?(name)
            raise(ArgumentError,"unknown arch: #{name}")
          end

          self.arch = Arch.send(name)
        end
      end
    end
  end
end
